/*
 * Created on 2006-6-8
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package com.powerunion.datacollection.report.excelreport;

import java.io.OutputStream;
import java.sql.Connection;
import java.util.HashMap;
import java.util.Map;

import com.powerunion.datacollection.report.excelreport.config.DataSourceConfig;
import com.powerunion.datacollection.report.excelreport.config.ReportConfig;
import com.powerunion.datacollection.report.excelreport.config.ReportConfigManager;
import com.powerunion.datacollection.report.excelreport.datasource.CommonDBDataSource;
import com.powerunion.datacollection.report.excelreport.datasource.DBConnection;
import com.powerunion.datacollection.report.excelreport.datasource.IDataSource;
import com.powerunion.datacollection.report.excelreport.datasource.StaticDataSource;

/**
 * &nbsp;&nbsp;&nbsp;&nbsp;ReportEngine是整个报表生成器的核心，用户通过使用ReportEngine来配置并生成报表文件。
 * 这个类提供一系列接口，用户用户在生成报表前配置相关参数信息：比如：配置数据源实现，
 * 配置数据连接对象以及配置文件路径等信息。<br>
 * 下面是一个简单应用的实例：<br>
 * <pre>
 * 	ReportEngine reportEngine = null;
   	//构造报表引擎对象
 * 	reportEngine = ReportEngine.getReportEngine("ReportConfig.xml",false);
   	//设置报表模板文件目录
 * 	reportEngine.setTempletFilePath(".");
   	//设置默认数据源的数据库连接信息。（如果实现自己定义的数据源此处不需要配置，
 * 	因为数据库连接由自定义的数据源来负责实现。）
 * 	reportEngine.setDBConnection(new DBConnection("java:/comp/env/jdbc/as");
 * 	//配置sutdentGrade报表打印所需要的报表参数
 * 	Map params = new HashMap();
 * 	params.put("report", "sutdentGrade");//报表名称设置
 * 	params.put("arg1", "50");
 * 	params.put("author", "余俊");
 * 	//生成报表
 * 	File outFile = new File("report.xls");
 * 	FileOutputStream out = new FileOutputStream(outFile);
 * 	reportEngine.getReportAsExcel(out, params);
 * 	out.close();
 * </pre>
 * @author juny
 */
public class ReportEngine {
    private static Logger log = Logger.getLogger(ReportEngine.class);
    private ReportEngine(){
        dsImplMap = new HashMap();
    }
//    public static ReportManager getReportManager(){
//        if(null == reportManager){
//            reportManager = new ReportManager(); 
//        }
//        return reportManager;
//    }
    
    /**
     * 取得报表引擎对象。<br>
     * 构建报表引擎对象，并装载配置文件信息。
     * @param fileName	配置文件名。
     * @return 	返回ReportEngine
     */
    public static synchronized ReportEngine getReportEngine(String fileName){
        if(null != reportEngine){
            return reportEngine;
        }
        //synchronized(reportManager){
	        if(null == reportEngine){
	            reportEngine = new ReportEngine(); 
	            reportEngine.setConfigFile(fileName);
	        }
        //}
        return reportEngine;
    }
    
    /**
     * 取得报表引擎对象。
     * @param fileName:传入配置文件名称
     * @param reloadConfig:true --重新装载配置文件，false --不重新装载配置文件。
     * @return 返回报表引擎对象。
     */
    public static synchronized ReportEngine getReportEngine(String fileName, boolean reloadConfig){
        if(null == reportEngine  || reloadConfig){
            if(reloadConfig){
                if(log.isInfoEnable()){
                    log.info("Reloading excel report config file.");
                }
            }
            reportEngine = new ReportEngine(); 
            reportEngine.setConfigFile(fileName);
        }
        return reportEngine;
    }
    
    /**
     * 设置模板文件目录
     * @param path:设置模板文件目录.
     */
    public void setTempletFilePath(String path){
        reportConfigManager.setTempletFilePath(path);
    }
    
    /**
     * 设置Logger的实现类名。<br>
     * 
     * @param className logger的类名 注意：该类必须继承至Logger接口
     * @see Logger
     */
    public static void setLogger(String className){
        logClassName = className;
    }
    private static String logClassName = null;
    
    /**
     * 取得用户配置的logger实现类名。在Logger类中使用。
     * @return 
     */
    static String getLogger(){
        return logClassName;
    }
    
    /**
     * 生成报表函数，该函数将根据传入参数信息，生成Excel报表文件，并将生成的文件信息输出
     * 到指定的输出流当中(os)
     * @param os		生成报表数据的输出流
     * @param params	生成报表需要的输入参数。<br>
     * 					注意：params不能为空，因为params中至少有一个参数传入报表名称
     * @return
     * @throws Exception
     * @see net.excel.report.config.ReportConfig.REPORT
     */
    public boolean getReportAsExcel(OutputStream os, Map params)
									throws Exception {
        if(null == os || null == params){
            throw new Exception("One or more null paramaters were passed into the ReportManager.getReportAsExcel function!");
        }
        
	    //取得报表名称
	    String reportName = (String)params.get(ReportConfig.REPORT);
	    if(null == reportName || "".equals(reportName)){
	        throw new Exception("Couldn't find any report name. Please pass the correct report name which do you want!");
	    }
	    
	    if(log.isDebugEnable()){
	        log.debug("Report name = " + reportName);
	    }
	    
	    //取得该报表对应的所有数据源配置信息.
	    ReportConfig reportConfig = reportConfigManager.getReport(reportName);
	    if (null == reportConfig){
	        throw new Exception("Unknown report name :" + reportName );
	    }
	    
	    Report report = new Report(this);
	    try{
		    report.initializeReport(reportConfig, params);
		    if(log.isDebugEnable()){
		        log.debug("Writing report data.....");
		    }
		    //生成报表对象
		    report.writeData(os);
	    }finally{
	        report.release();
	    }
	    return true;
    }
    
    /**
     * 生成报表函数，该函数将根据传入参数信息，生成Excel报表文件，并将生成的文件信息输出
     * 到指定的输出流当中(os)
     * @param os			生成报表数据的输出流
     * @param reportName	要生成报表的名称
     * @param params		生成报表需要的输入参数。
     * @return
     * @throws Exception
     */
    public boolean getReportAsExcel(OutputStream os, 
            						String reportName, 
            						Map params)
							throws Exception {
        if(null == reportName || "".equals(reportName)){
            throw new Exception("Couldn't find any report name. Please pass the correct report name which do you want!");
        }
        
        if(null == params){
            params = new HashMap();
        }
        
        params.put(ReportConfig.REPORT, reportName);
        return getReportAsExcel(os, params);
    }
    
    /**
     * 设置数据源实现对象, 对于每种类型的数据源对象都有一个对应的数据源对象的实现，
     * 当在外部设置了该数据源对象的实现时，报表框架在创建各个实际的数据源时会根据
     * 数据源实现对象。如果没有指定，则每一种类型的数据源都有一个默认的数据实现对象
     * 与之对应。<br>
     * 例如：<br>
     *  ......<br><br>
     *  //配置用户自定义的数据源实现。<br>
     * UserDefineDBDataSourceImpl dsImpl = new UserDefineDBDataSourceImpl();<br>
     * reportEngine.setDataSourceImplement(DataSourceConfig.DS_TYPE_DATABASE, 
	 *	                (BaseDataSource)dsImpl);<br><br>
     * UserDefineStaticDataSourceImpl staticDsImpl = new UserDefineStaticDataSourceImpl();
     * reportEngine.setDataSourceImplement(DataSourceConfig.DS_TYPE_STATIC, 
	 *	                (BaseDataSource)staticDsImpl);<br><br>
	 * ......
     * @param dsType: 数据源实现类型。(参见DataSourceConfig)<br>
     * 			目前有两种类型的实现：<br>
     * 			1. DataSourceConfig.DS_TYPE_DATABASE 用户自定义数据库类型数据源实现。<br>
     *          2. DataSourceConfig.DS_TYPE_STATIC 用户自定义静态类型数据源实现类<br>
     * @param dsImpl: 具体实现数据源对象。（Type：IDataSource. 注意：实现的数据源对象必须继承至BaseDataSource，
     * 但目前程序在参数传递没加限定,使用时务必注意。具体原因可以参见BaseDataSource类）
     * @return true 设置成功； false 设置失败。
     * @see net.excel.report.datasource.BaseDataSource
     */
    public boolean setDataSourceImplement(String dsType, IDataSource dsImpl){
        if(DataSourceConfig.isSupportedType(dsType)){
            dsImplMap.put(dsType, dsImpl);
            return true;
        }
        return false;
    }
    
    /**
     * 设置数据库连接配置对象；关于数据库连接对象的使用可以参见开始出的示例。
     * @param con:数据库连接对象
     */
    public void setDBConnection(DBConnection con){
        this.dbConnection = con;
    }
    
    /**
     * 装载配置文件信息. 并读取配置文件信息.
     * @param fileName：配置文件名称（全路径表示）。
     */
    private void setConfigFile(String fileName){
        this.fileName = fileName;
        if(null == reportConfigManager){
            reportConfigManager = new ReportConfigManager(fileName);
        }
    }
    
    /**
     * 根据数据源配置信息,创建一个新的数据源对象实例。
     * @param ds：数据源配置对象，（Type: DataSourceConfig）
     * @param name：新数据源实例的名称。
     * @return 成功：则返回生成的数据源对象；失败：则返回null。
     * @throws Exception
     */
    public IDataSource getDataSourceInstance(DataSourceConfig ds, 
            							String name) throws Exception{
        String dsType = ds.getDataSourceType();
        if(log.isDebugEnable()){
	        log.debug("Getting datasource instances. Data source type is [" 
	                + dsType 
	                + "] and data source name is " + name);
        }
        
        //查找用户是否定义了该类型的数据源实现。
        IDataSource dsInstance = (IDataSource)this.dsImplMap.get(dsType);
        if(null != dsInstance){
            Object obj = dsInstance.getClass().newInstance();
            dsInstance = (IDataSource) obj;
            dsInstance.setConfigInfo(ds, name);
        }
        
        //上一步没找到则查找默认的实现。
        if(null == dsInstance){
            log.debug("Getting datasource instance from default implement.");
            dsInstance = getDefualtDSInstance(ds, name);
        }
        
        //如果不能取得数据源实现对象，显示错误信息。。并抛出异常信息。
        if(null == dsInstance){
            String errMsg = "Could not find any datasource object!" +
    						"Data source name is " + name;
            log.error(errMsg);
            throw new Exception(errMsg);
        }
        
        //初始化数据源对象。
        dsInstance.initialize();
        
        return dsInstance;
    }
    
    /**
     * 取得系统默认的数据源实现。
     * @param ds: 数据源配置信息对象.(Type: DataSourceConfig)
     * @param name: 数据源实例名称。
     * @return 创建成功则返回一个新的数据源对象实例；失败则返回null。
     * @throws Exception
     */
    private IDataSource getDefualtDSInstance(DataSourceConfig ds, String name) throws Exception{
        String dsType = ds.getDataSourceType();
        if(dsType.equals(DataSourceConfig.DS_TYPE_DATABASE)){
            CommonDBDataSource commds = new CommonDBDataSource(ds, name);
            //设置默认的数据库连接对象。
            commds.setConnection(getConnection());
            return commds;
        }
        
        if(dsType.equals(DataSourceConfig.DS_TYPE_STATIC)){
            return new StaticDataSource(ds, name);
        }
        
        log.warn("System could not find any datasource object implements! " +
        		"data source name is " + name
        		);
        
        return null;
    }
    
    /**
     * 取得数据库连接对象。调用该函数可以取得用户设置的数据库连接。
     * @return 成功 返回一个有效的数据库连接； 失败 抛出异常信息。
     * @throws Exception 调用该函数如果用户没有配置有效的连接则抛出异常信息。
     */
    private Connection getConnection() throws Exception{
        if(null != dbConnection){
            return dbConnection.getConnection();
        }else{
            throw new Exception("Can't find the instance of the DBConnection.");
        }
    }
    
    /**
     * 关闭数据库连接对象。
     * @throws Exception
     */
    private void closeConnection() throws Exception{
        if(null != dbConnection){
            //this.connection
        }
    }
    
    /**
     * 取得报表配置对象实例。调用该函数从参数map种取得用户请求的报表名称，并根据该名称来取得
     * 在配置文件中报表配置信息对象。
     * @param params：参数Map。
     * @return 成功 返回报表配置对象。 失败 返回空。
     */
    private ReportConfig getReportConfig(Map params){
        //取得报表名称
        String reportName = (String)params.get(ReportConfig.REPORT);
        if(null == reportName || "".equals(reportName))
            return null;
        
        //取得该报表对应的所有数据源配置信息.
        ReportConfig report = reportConfigManager.getReport(reportName);
        return report;
    }
    
    private Map dsImplMap = null; //保存各种不同风格数据源的对应实现对象
    private String fileName = null;
    private static ReportEngine reportEngine = null;
    private ReportConfigManager reportConfigManager = null;
    private DBConnection dbConnection = null;
}
